home *** CD-ROM | disk | FTP | other *** search
/ Isometric Game Programming with DirectX 7.0 / Isometric Game Programming.iso / source / chapter19 / isohex19_2 / isohex19_2.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-07  |  42.7 KB  |  1,386 lines

  1. /*****************************************************************************
  2. IsoHex19_2.cpp
  3. Ernest S. Pazera
  4. 04OCT2000
  5. Start a WIN32 Application Workspace, add in this file
  6. Requires the following libs:
  7. ddraw.lib, dxguid.lib
  8. Requires the following files:
  9. DDFuncs.h.cpp, GDICanvas.h/cpp, IsoMouseMap.h/cpp,
  10. IsoScroller.h/cpp, IsoTilePlotter.h/cpp, IsoTileWalker.h/cpp
  11. TileSet.h/cpp. IsoHexCore.h, IsoHexDefs.h
  12. IsoRenderer.h/cpp
  13. *****************************************************************************/
  14.  
  15. //////////////////////////////////////////////////////////////////////////////
  16. //INCLUDES
  17. //////////////////////////////////////////////////////////////////////////////
  18. #define WIN32_LEAN_AND_MEAN  
  19.  
  20. #include <windows.h>  
  21. #include "DDFuncs.h"
  22. #include "TileSet.h"
  23. #include "IsoHexCore.h"
  24. #include "IsoRenderer.h"
  25. #include <list>
  26.  
  27. //////////////////////////////////////////////////////////////////////////////
  28. //DEFINES
  29. //////////////////////////////////////////////////////////////////////////////
  30. //name for our window class
  31. #define WINDOWCLASS "ISOHEX19"
  32. //title of the application
  33. #define WINDOWTITLE "IsoHex 19-2"
  34.  
  35. const int MAPWIDTH=40;
  36. const int MAPHEIGHT=40;
  37.  
  38. //gamestates
  39. const int GS_IDLE=0;//waits for a keypress
  40. const int GS_STARTTURN=1;//starts a players turn
  41. const int GS_ENDTURN=2;//ends a players turn
  42. const int GS_NEXTUNIT=3;//finds the next unit to move
  43. const int GS_STARTMOVE=4;//starts moving the unit
  44. const int GS_DOMOVE=5;//moves the unit
  45. const int GS_ENDMOVE=6;//ends a unit move
  46. const int GS_NULLMOVE=7;//tells a unit to not move
  47. const int GS_SKIPMOVE=8;//temporarily skip the move
  48. const int GS_HOLDPOSITION=9;//fortify
  49. const int GS_CLICKSELECT=10;//when a click occurs, this gamestate is selected
  50. const int GS_CLICKCENTER=11;//click on a map location with none of the players units
  51. const int GS_CLICKSTACK=12;//click on a map location with at least two of the players units
  52. const int GS_PICKUNIT=13;//pick unit from the selection window
  53.  
  54. //////////////////////////////////////////////////////////////////////////////
  55. //TYPEDEFS/STRUCTS
  56. //////////////////////////////////////////////////////////////////////////////
  57. struct UnitInfo//unit information structure
  58. {
  59.     int iType;//type of unit
  60.     int iTeam;//team to which the unit belongs
  61.     POINT ptPosition;//map location of the unit
  62.     int iMovePoints;//number of movepoints left this turn
  63.     bool bHolding;//true if holding position
  64. };
  65.  
  66. typedef UnitInfo *PUNITINFO;//pointer type alias for unitinfo
  67.  
  68. typedef std::list<PUNITINFO> UNITLIST;//list of units
  69. typedef std::list<PUNITINFO>::iterator UNITLISTITER;//iterator for unit list
  70.  
  71. //////////////////////////////////////////////////////////////////////////////
  72. //PROTOTYPES
  73. //////////////////////////////////////////////////////////////////////////////
  74. bool Prog_Init();//game data initalizer
  75. void Prog_Loop();//main game loop
  76. void Prog_Done();//game clean up
  77.  
  78. //minimap functions
  79. void SetupMiniMap();//does initial setup of the minimap
  80. void UpdateMiniMap();//updates the minimap to reflect current game status
  81. void RedrawMiniMap();//redraws the minimap to reflect current gamestate
  82. void ShowMiniMap();//shows the minimap on-screen
  83. void DestroyMiniMap();//cleans up the minimap
  84.  
  85. //////////////////////////////////////////////////////////////////////////////
  86. //GLOBALS
  87. //////////////////////////////////////////////////////////////////////////////
  88. HINSTANCE hInstMain=NULL;//main application handle
  89. HWND hWndMain=NULL;//handle to our main window
  90.  
  91. //game state
  92. int iGameState=GS_IDLE;
  93. int iCurrentTeam=0;//the team whose turn it currently is
  94.  
  95. //directdraw
  96. LPDIRECTDRAW7 lpdd=NULL;
  97. LPDIRECTDRAWSURFACE7 lpddsMain=NULL;
  98. LPDIRECTDRAWSURFACE7 lpddsBack=NULL;
  99. LPDIRECTDRAWSURFACE7 lpddsFrame=NULL;
  100. LPDIRECTDRAWSURFACE7 lpddsMiniMap=NULL;//minimap surface
  101.  
  102. //tilesets
  103. CTileSet tsBack;//background
  104. CTileSet tsUnit;//unit
  105. CTileSet tsShield;//shields
  106. CTileSet tsPressEnter;//press enter text
  107. CTileSet tsMiniMap;//minimap tileset
  108.  
  109. //offsets for the shields
  110. POINT ptShieldOffset[2];//one for each unit
  111.  
  112. //isohexcore components
  113. CTilePlotter TilePlotter;//plotter
  114. CTileWalker TileWalker;//walker
  115. CScroller Scroller;//scroller
  116. CMouseMap MouseMap;//mousemap
  117. CRenderer Renderer;//renderer
  118.  
  119. //minimap components
  120. CTilePlotter MiniTilePlotter;
  121. CScroller MiniScroller;
  122.  
  123. POINT ptScroll;//keep track of how quickly we scroll
  124. POINT ptClick;//keep track of the clicked position
  125.  
  126. //map location structure
  127. struct MapLocation
  128. {
  129.     UNITLIST ulUnitList;//list of units on this map location
  130. };
  131.  
  132. MapLocation mlMap[MAPWIDTH][MAPHEIGHT];//map array
  133. UNITLIST MainUnitList;//unit list for all units
  134. UNITLIST TeamUnitList;//unit list for teams(Current Players Turn)
  135. PUNITINFO pCurrentUnit;//current unit being moved
  136. bool bFlash;//controls the flashing of the current unit
  137. ISODIRECTION idMoveUnit;//direction in which the unit will be moved
  138. bool bMovedUnit;//indicates whether or not a unit has been moved by the current player this turn
  139.  
  140. //unit selection variables(for selecting stacks of units)
  141. RECT rcSelectWindow;//the selection window
  142. POINT ptCellSize;//size of selection cell
  143. PUNITINFO SelectUnitList[20];//unit selection list (max of 20 units)
  144. POINT ptUnitOffset;//offset for placing units in the selection window
  145. DWORD dwSelectWindowColor;//color for the selection window
  146.  
  147. //minimap array
  148. int* MiniMap;//current minimap array
  149.  
  150. //rendering functionprototype
  151. void RenderFunc(LPDIRECTDRAWSURFACE7 lpddsDst,RECT* rcClip,int xDst,int yDst,int xMap,int yMap);
  152.  
  153. //////////////////////////////////////////////////////////////////////////////
  154. //WINDOWPROC
  155. //////////////////////////////////////////////////////////////////////////////
  156. LRESULT CALLBACK TheWindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
  157. {
  158.     //which message did we get?
  159.     switch(uMsg)
  160.     {
  161.     case WM_LBUTTONDOWN://beginning of click-select
  162.         {
  163.             //process differently, depending on gamestate
  164.             switch(iGameState)
  165.             {
  166.             case GS_IDLE:
  167.                 {
  168.                     //grab the mouse position
  169.                     POINT ptCursor;
  170.                     ptCursor.x=LOWORD(lParam);
  171.                     ptCursor.y=HIWORD(lParam);
  172.                     //use the mousemap to get click position
  173.                     ptClick=MouseMap.MapMouse(ptCursor);
  174.                 }break;
  175.             }
  176.             return(0);//handled
  177.         }break;
  178.     case WM_LBUTTONUP://end of click-select
  179.         {
  180.             //process differently, depending on gamestate
  181.             switch(iGameState)
  182.             {
  183.             case GS_IDLE:
  184.                 {
  185.                     //grab the mouse position
  186.                     POINT ptCursor;
  187.                     ptCursor.x=LOWORD(lParam);
  188.                     ptCursor.y=HIWORD(lParam);
  189.                     //use the mousemap to get click position
  190.                     POINT ptMap=MouseMap.MapMouse(ptCursor);
  191.                     //check map position against ptClick
  192.                     if(ptMap.x==ptClick.x && ptMap.y==ptClick.y)
  193.                     {
  194.                         //set gamestate to GS_CLICKSELECT
  195.                         iGameState=GS_CLICKSELECT;
  196.                     }
  197.                 }break;
  198.             case GS_PICKUNIT:
  199.                 {
  200.                     //grab the mouse position
  201.                     POINT ptMouse;
  202.                     ptMouse.x=LOWORD(lParam);
  203.                     ptMouse.y=HIWORD(lParam);
  204.                     //check to see if the click was within the select window
  205.                     if(PtInRect(&rcSelectWindow,ptMouse))
  206.                     {
  207.                         //within the select window
  208.                         //determine which cell was clicked
  209.                         ptMouse.x-=rcSelectWindow.left;//subtract top left of the window
  210.                         ptMouse.y-=rcSelectWindow.top;
  211.                         ptMouse.x/=ptCellSize.x;//divide by cellsize
  212.                         ptMouse.y/=ptCellSize.y;
  213.                         int cellnum=ptMouse.x+5*ptMouse.y;//calc cell number
  214.                         //check for a NULL
  215.                         if(SelectUnitList[cellnum]==NULL)
  216.                         {
  217.                             //empty cell
  218.                             //do nothing
  219.                             return(0);
  220.                         }
  221.                         else
  222.                         {
  223.                             //non-empty cell
  224.                             //check if the unit has any movement points left
  225.                             if(SelectUnitList[cellnum]->iMovePoints==0)
  226.                             {
  227.                                 //no movement points
  228.                                 //do nothing
  229.                                 return(0);
  230.                             }
  231.                             else
  232.                             {
  233.                                 //check for holding position
  234.                                 if(SelectUnitList[cellnum]->bHolding)
  235.                                 {
  236.                                     //set holding to false
  237.                                     SelectUnitList[cellnum]->bHolding=false;
  238.                                 }
  239.                                 //remove this unit from the team list
  240.                                 TeamUnitList.remove(SelectUnitList[cellnum]);
  241.                                 //re-add this unit to the team list at the beginning
  242.                                 TeamUnitList.push_front(SelectUnitList[cellnum]);
  243.                                 //select next unit
  244.                                 iGameState=GS_NEXTUNIT;
  245.                                 //add entire screen to update rect
  246.                                 Renderer.AddRect(Scroller.GetScreenSpace());
  247.                             }
  248.                         }
  249.                     }
  250.                     else
  251.                     {
  252.                         //outside of select window
  253.                         iGameState=GS_NEXTUNIT;
  254.                     }
  255.                 }break;
  256.             }
  257.             return(0);//handled
  258.         }break;
  259.     case WM_KEYDOWN:
  260.         {
  261.             switch(wParam)
  262.             {
  263.             case VK_ESCAPE:
  264.                 {
  265.                     DestroyWindow(hWndMain);
  266.                     return(0);
  267.                 }break;
  268.             case VK_RETURN://at end of turn, if no unit has been moved
  269.                 {
  270.                     if(iGameState==GS_IDLE && pCurrentUnit==NULL) iGameState=GS_ENDTURN;
  271.                     return(0);
  272.                 }break;
  273.             case VK_SPACE://do not move unit
  274.                 {
  275.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL) iGameState=GS_NULLMOVE;//only respond when gamestate is GS_IDLE;
  276.                     return(0);
  277.                 }break;
  278.             case 'W'://wait to move this unit later
  279.                 {
  280.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL) iGameState=GS_SKIPMOVE;//skip this unit for now
  281.                 }break;
  282.             case 'H'://tell unit to hold position
  283.                 {
  284.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL) iGameState=GS_HOLDPOSITION;//tell the unit to hold position
  285.                 }break;
  286.             case 'C'://center on the current unit
  287.                 {
  288.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)
  289.                     {
  290.                         //plot the current unit's world position
  291.                         POINT ptPlot=TilePlotter.PlotTile(pCurrentUnit->ptPosition);
  292.                         //adjust by half width and height of the screen
  293.                         ptPlot.x-=(Scroller.GetScreenSpaceWidth()/2);
  294.                         ptPlot.y-=(Scroller.GetScreenSpaceHeight()/2);
  295.                         //set the anchor
  296.                         Scroller.SetAnchor(&ptPlot);
  297.                         //add update rect to renderer
  298.                         Renderer.AddRect(Scroller.GetScreenSpace());
  299.                     }
  300.                 }break;
  301.             case VK_NUMPAD8:
  302.             case VK_UP:
  303.                 {
  304.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  305.                     {
  306.                         idMoveUnit=ISO_NORTH;//move to the north
  307.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  308.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  309.                         {
  310.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  311.                             {
  312.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  313.                             }
  314.                             else
  315.                             {
  316.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  317.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  318.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  319.                                 {
  320.                                     iGameState=GS_STARTMOVE;
  321.                                 }
  322.                             }
  323.                         }
  324.                     }
  325.                 }break;
  326.             case VK_NUMPAD9:
  327.             case VK_PRIOR:
  328.                 {
  329.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  330.                     {
  331.                         idMoveUnit=ISO_NORTHEAST;//move to the north
  332.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  333.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  334.                         {
  335.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  336.                             {
  337.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  338.                             }
  339.                             else
  340.                             {
  341.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  342.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  343.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  344.                                 {
  345.                                     iGameState=GS_STARTMOVE;
  346.                                 }
  347.                             }
  348.                         }
  349.                     }
  350.                 }break;
  351.             case VK_NUMPAD6:
  352.             case VK_RIGHT:
  353.                 {
  354.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  355.                     {
  356.                         idMoveUnit=ISO_EAST;//move to the north
  357.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  358.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  359.                         {
  360.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  361.                             {
  362.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  363.                             }
  364.                             else
  365.                             {
  366.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  367.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  368.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  369.                                 {
  370.                                     iGameState=GS_STARTMOVE;
  371.                                 }
  372.                             }
  373.                         }
  374.                     }
  375.                 }break;
  376.             case VK_NUMPAD3:
  377.             case VK_NEXT:
  378.                 {
  379.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  380.                     {
  381.                         idMoveUnit=ISO_SOUTHEAST;//move to the north
  382.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  383.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  384.                         {
  385.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  386.                             {
  387.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  388.                             }
  389.                             else
  390.                             {
  391.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  392.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  393.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  394.                                 {
  395.                                     iGameState=GS_STARTMOVE;
  396.                                 }
  397.                             }
  398.                         }
  399.                     }
  400.                 }break;
  401.             case VK_NUMPAD2:
  402.             case VK_DOWN:
  403.                 {
  404.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  405.                     {
  406.                         idMoveUnit=ISO_SOUTH;//move to the north
  407.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  408.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  409.                         {
  410.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  411.                             {
  412.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  413.                             }
  414.                             else
  415.                             {
  416.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  417.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  418.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  419.                                 {
  420.                                     iGameState=GS_STARTMOVE;
  421.                                 }
  422.                             }
  423.                         }
  424.                     }
  425.                 }break;
  426.             case VK_NUMPAD1:
  427.             case VK_END:
  428.                 {
  429.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  430.                     {
  431.                         idMoveUnit=ISO_SOUTHWEST;//move to the north
  432.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  433.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  434.                         {
  435.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  436.                             {
  437.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  438.                             }
  439.                             else
  440.                             {
  441.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  442.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  443.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  444.                                 {
  445.                                     iGameState=GS_STARTMOVE;
  446.                                 }
  447.                             }
  448.                         }
  449.                     }
  450.                 }break;
  451.             case VK_NUMPAD4:
  452.             case VK_LEFT:
  453.                 {
  454.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  455.                     {
  456.                         idMoveUnit=ISO_WEST;//move to the north
  457.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  458.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  459.                         {
  460.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  461.                             {
  462.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  463.                             }
  464.                             else
  465.                             {
  466.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  467.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  468.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  469.                                 {
  470.                                     iGameState=GS_STARTMOVE;
  471.                                 }
  472.                             }
  473.                         }
  474.                     }
  475.                 }break;
  476.             case VK_NUMPAD7:
  477.             case VK_HOME:
  478.                 {
  479.                     if(iGameState==GS_IDLE && pCurrentUnit!=NULL)//gamestate must be GS_IDLE
  480.                     {
  481.                         idMoveUnit=ISO_NORTHWEST;//move to the north
  482.                         POINT ptNext=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);//check the next position
  483.                         if(ptNext.x>=0 && ptNext.y>=0 && ptNext.x<MAPWIDTH && ptNext.y<MAPWIDTH)//bounds checking
  484.                         {
  485.                             if(mlMap[ptNext.x][ptNext.y].ulUnitList.empty())//if the map location is empty
  486.                             {
  487.                                 iGameState=GS_STARTMOVE;//set the unit in motion
  488.                             }
  489.                             else
  490.                             {
  491.                                 UNITLISTITER iter=mlMap[ptNext.x][ptNext.y].ulUnitList.begin();//get the first entry in the list
  492.                                 PUNITINFO pUnitInfo=*iter;//get the unit from the list
  493.                                 if(pUnitInfo->iTeam==pCurrentUnit->iTeam)//must be the same team
  494.                                 {
  495.                                     iGameState=GS_STARTMOVE;
  496.                                 }
  497.                             }
  498.                         }
  499.                     }
  500.                 }break;
  501.             }
  502.         }break;
  503.     case WM_DESTROY://the window is being destroyed
  504.         {
  505.  
  506.             //tell the application we are quitting
  507.             PostQuitMessage(0);
  508.  
  509.             //handled message, so return 0
  510.             return(0);
  511.  
  512.         }break;
  513.     case WM_PAINT://the window needs repainting
  514.         {
  515.             //a variable needed for painting information
  516.             PAINTSTRUCT ps;
  517.             
  518.             //start painting
  519.             HDC hdc=BeginPaint(hwnd,&ps);
  520.  
  521.             /////////////////////////////
  522.             //painting code would go here
  523.             /////////////////////////////
  524.  
  525.             //end painting
  526.             EndPaint(hwnd,&ps);
  527.                         
  528.             //handled message, so return 0
  529.             return(0);
  530.         }break;
  531.     }
  532.  
  533.     //pass along any other message to default message handler
  534.     return(DefWindowProc(hwnd,uMsg,wParam,lParam));
  535. }
  536.  
  537.  
  538. //////////////////////////////////////////////////////////////////////////////
  539. //WINMAIN
  540. //////////////////////////////////////////////////////////////////////////////
  541. int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd)
  542. {
  543.     //assign instance to global variable
  544.     hInstMain=hInstance;
  545.  
  546.     //create window class
  547.     WNDCLASSEX wcx;
  548.  
  549.     //set the size of the structure
  550.     wcx.cbSize=sizeof(WNDCLASSEX);
  551.  
  552.     //class style
  553.     wcx.style=CS_OWNDC | CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
  554.  
  555.     //window procedure
  556.     wcx.lpfnWndProc=TheWindowProc;
  557.  
  558.     //class extra
  559.     wcx.cbClsExtra=0;
  560.  
  561.     //window extra
  562.     wcx.cbWndExtra=0;
  563.  
  564.     //application handle
  565.     wcx.hInstance=hInstMain;
  566.  
  567.     //icon
  568.     wcx.hIcon=LoadIcon(NULL,IDI_APPLICATION);
  569.  
  570.     //cursor
  571.     wcx.hCursor=LoadCursor(NULL,IDC_ARROW);
  572.  
  573.     //background color
  574.     wcx.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
  575.  
  576.     //menu
  577.     wcx.lpszMenuName=NULL;
  578.  
  579.     //class name
  580.     wcx.lpszClassName=WINDOWCLASS;
  581.  
  582.     //small icon
  583.     wcx.hIconSm=NULL;
  584.  
  585.     //register the window class, return 0 if not successful
  586.     if(!RegisterClassEx(&wcx)) return(0);
  587.  
  588.     //create main window
  589.     hWndMain=CreateWindowEx(0,WINDOWCLASS,WINDOWTITLE, WS_POPUP | WS_VISIBLE,0,0,320,240,NULL,NULL,hInstMain,NULL);
  590.  
  591.     //error check
  592.     if(!hWndMain) return(0);
  593.  
  594.     //if program initialization failed, then return with 0
  595.     if(!Prog_Init()) return(0);
  596.  
  597.     //message structure
  598.     MSG msg;
  599.  
  600.     //message pump
  601.     for(;;)    
  602.     {
  603.         //look for a message
  604.         if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
  605.         {
  606.             //there is a message
  607.  
  608.             //check that we arent quitting
  609.             if(msg.message==WM_QUIT) break;
  610.             
  611.             //translate message
  612.             TranslateMessage(&msg);
  613.  
  614.             //dispatch message
  615.             DispatchMessage(&msg);
  616.         }
  617.  
  618.         //run main game loop
  619.         Prog_Loop();
  620.     }
  621.     
  622.     //clean up program data
  623.     Prog_Done();
  624.  
  625.     //return the wparam from the WM_QUIT message
  626.     return(msg.wParam);
  627. }
  628.  
  629. //////////////////////////////////////////////////////////////////////////////
  630. //INITIALIZATION
  631. //////////////////////////////////////////////////////////////////////////////
  632. bool Prog_Init()
  633. {
  634.     //create IDirectDraw object
  635.     lpdd=LPDD_Create(hWndMain,DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT);
  636.  
  637.     //set display mode
  638.     lpdd->SetDisplayMode(640,480,16,0,0);
  639.  
  640.     //create primary surface
  641.     lpddsMain=LPDDS_CreatePrimary(lpdd,1);
  642.  
  643.     //get back buffer
  644.     lpddsBack=LPDDS_GetSecondary(lpddsMain);
  645.  
  646.     //create the frame buffer
  647.     lpddsFrame=LPDDS_CreateOffscreen(lpdd,640,480);
  648.  
  649.     //load in the mousemap
  650.     MouseMap.Load("MouseMap.bmp");
  651.  
  652.     //set up the tile plotter
  653.     TilePlotter.SetMapType(ISOMAP_DIAMOND);//diamond mode
  654.     TilePlotter.SetTileSize(MouseMap.GetWidth(),MouseMap.GetHeight());//grab width and height from mousemap
  655.  
  656.     //set up tile walker to diamond mode
  657.     TileWalker.SetMapType(ISOMAP_DIAMOND);
  658.  
  659.     //set up screeen space
  660.     RECT rcTemp;
  661.     SetRect(&rcTemp,0,0,480,480);
  662.     Scroller.SetScreenSpace(&rcTemp);
  663.  
  664.     //load in tiles and other images
  665.     tsBack.Load(lpdd,"backgroundts.bmp");
  666.     tsUnit.Load(lpdd,"units.bmp");
  667.     tsShield.Load(lpdd,"shields.bmp");
  668.     tsPressEnter.Load(lpdd,"PressEnter.bmp");
  669.  
  670.     //calculate the shield offsets
  671.     ptShieldOffset[0].x=tsUnit.GetTileList()[0].rcSrc.right-tsUnit.GetTileList()[0].ptAnchor.x;
  672.     ptShieldOffset[0].y=tsUnit.GetTileList()[0].rcSrc.top-tsUnit.GetTileList()[0].ptAnchor.y;
  673.     ptShieldOffset[1].x=tsUnit.GetTileList()[1].rcSrc.right-tsUnit.GetTileList()[1].ptAnchor.x;
  674.     ptShieldOffset[1].y=tsUnit.GetTileList()[1].rcSrc.top-tsUnit.GetTileList()[1].ptAnchor.y;
  675.  
  676.     //grab tile extent from tileset
  677.     CopyRect(&rcTemp,&tsBack.GetTileList()[0].rcDstExt);
  678.  
  679.     //calculate the worldspace
  680.     Scroller.CalcWorldSpace(&TilePlotter,&rcTemp,MAPWIDTH,MAPHEIGHT);
  681.  
  682.     //calculate the mousemap reference point
  683.     MouseMap.CalcReferencePoint(&TilePlotter,&rcTemp);
  684.  
  685.     //calculate anchor space
  686.     Scroller.CalcAnchorSpace();
  687.  
  688.     //set wrap modes for scroller
  689.     Scroller.SetHWrapMode(WRAPMODE_CLIP);
  690.     Scroller.SetVWrapMode(WRAPMODE_CLIP);
  691.  
  692.     //set scroller anchor to (0,0)
  693.     Scroller.GetAnchor()->x=0;
  694.     Scroller.GetAnchor()->y=0;
  695.  
  696.     //attach scrolelr and tilewalker to mousemap
  697.     MouseMap.SetScroller(&Scroller);
  698.     MouseMap.SetTileWalker(&TileWalker);
  699.  
  700.     //set up the map to a random tilefield
  701.     int x;
  702.     int y;
  703.     for(x=0;x<MAPWIDTH;x++)
  704.     {
  705.         for(y=0;y<MAPHEIGHT;y++)
  706.         {
  707.             mlMap[x][y].ulUnitList.clear();//clear out the list for this map location
  708.         }
  709.     }
  710.  
  711.     //place the units
  712.     int team;
  713.     int count;
  714.     PUNITINFO pUnitInfo;
  715.     POINT ptTest;
  716.     for(team=0;team<2;team++)//place units for both teams
  717.     {
  718.         for(count=0;count<20;count++)//place 20 units for each team
  719.         {
  720.             //check for a valid placement
  721.             bool found=false;
  722.             while(!found)
  723.             {
  724.                 //random placement
  725.                 ptTest.x=rand()%MAPWIDTH;
  726.                 ptTest.y=rand()%MAPHEIGHT;
  727.                 //check for a unit at that position
  728.                 if(mlMap[ptTest.x][ptTest.y].ulUnitList.empty())
  729.                 {
  730.                     //unit list at location is empty... you can place the unit
  731.                     found=true;
  732.                 }
  733.                 else
  734.                 {
  735.                     //unit list is not empty, so must be occupied by the same team to be a valid location
  736.                     UNITLISTITER iter=mlMap[ptTest.x][ptTest.y].ulUnitList.begin();//get iterator to beginning of list
  737.                     pUnitInfo=*iter;//grab the items stored
  738.                     //check for the same team
  739.                     if(pUnitInfo->iTeam==team)
  740.                     {
  741.                         //same team, valid location
  742.                         found=true;
  743.                     }
  744.  
  745.                 }
  746.             }
  747.             //create the unit
  748.             pUnitInfo=new UnitInfo;
  749.             pUnitInfo->iTeam=team;//team
  750.             pUnitInfo->iType=rand()%2;//type
  751.             pUnitInfo->bHolding=false;//not holding position
  752.             pUnitInfo->ptPosition=ptTest;//location
  753.             //place unit in the main list
  754.             MainUnitList.push_back(pUnitInfo);
  755.             //place unit on the map
  756.             mlMap[pUnitInfo->ptPosition.x][pUnitInfo->ptPosition.y].ulUnitList.push_back(pUnitInfo);
  757.         }
  758.     }
  759.  
  760.     //calculate the extent rect
  761.     RECT rcExtent;
  762.     CopyRect(&rcExtent,&tsBack.GetTileList()[0].rcDstExt);//set to background extent
  763.     UnionRect(&rcExtent,&rcExtent,&tsUnit.GetTileList()[0].rcDstExt);//union with unit extent
  764.     rcExtent.right+=tsShield.GetTileList()[0].rcDstExt.right;//adjust the extent for the width of the shield
  765.  
  766.     //set up the renderer
  767.     Renderer.SetBackBuffer(lpddsBack);
  768.     Renderer.SetExtentRect(&rcExtent);
  769.     Renderer.SetFrameBuffer(lpddsFrame);
  770.     Renderer.SetMapSize(MAPWIDTH,MAPHEIGHT);
  771.     Renderer.SetMouseMap(&MouseMap);
  772.     Renderer.SetPlotter(&TilePlotter);
  773.     Renderer.SetRenderFunction(RenderFunc);
  774.     Renderer.SetScroller(&Scroller);
  775.     Renderer.SetUpdateRectCount(100);
  776.     Renderer.SetWalker(&TileWalker);
  777.  
  778.     //set the initial gamestate
  779.     iGameState=GS_STARTTURN;
  780.     iCurrentTeam=0;//current team
  781.     TeamUnitList.clear();//clear out the team's unit list
  782.  
  783.     //set up the selection window variables
  784.     DDPIXELFORMAT ddpf;
  785.     DDPF_Clear(&ddpf);
  786.     lpddsMain->GetPixelFormat(&ddpf);//grab pixel format
  787.     ddpf.dwRBitMask=(ddpf.dwRBitMask*3/4)&(ddpf.dwRBitMask);//calculate 3/4 red
  788.     ddpf.dwGBitMask=(ddpf.dwGBitMask*3/4)&(ddpf.dwGBitMask);//calc 3/4 green
  789.     ddpf.dwBBitMask=(ddpf.dwBBitMask*3/4)&(ddpf.dwBBitMask);//calc 3/4 blue
  790.     dwSelectWindowColor=ddpf.dwBBitMask | ddpf.dwRBitMask | ddpf.dwGBitMask;//make select window color
  791.  
  792.     //calculate the cell size
  793.     RECT rcCell;
  794.     CopyRect(&rcCell,&tsUnit.GetTileList()[0].rcDstExt);
  795.     UnionRect(&rcCell,&rcCell,&tsUnit.GetTileList()[1].rcDstExt);
  796.     rcCell.right+=(tsShield.GetTileList()[0].rcDstExt.right-tsShield.GetTileList()[0].rcDstExt.left);
  797.     //cell size
  798.     ptCellSize.x=rcCell.right-rcCell.left;
  799.     ptCellSize.y=rcCell.bottom-rcCell.top;
  800.     //unit offset
  801.     ptUnitOffset.x=-rcCell.left;
  802.     ptUnitOffset.y=-rcCell.top;
  803.     //calculate select window rect
  804.     SetRect(&rcSelectWindow,0,0,ptCellSize.x*5,ptCellSize.y*4);
  805.     //center the select window
  806.     OffsetRect(&rcSelectWindow,320-rcSelectWindow.right/2,240-rcSelectWindow.bottom/2);
  807.     
  808.     //update the entire screenspace
  809.     Renderer.AddRect(Scroller.GetScreenSpace());
  810.  
  811.     //setup the minimap
  812.     SetupMiniMap();
  813.     UpdateMiniMap();
  814.     RedrawMiniMap();
  815.     ShowMiniMap();
  816.  
  817.     return(true);//return success
  818. }
  819.  
  820. //////////////////////////////////////////////////////////////////////////////
  821. //CLEANUP
  822. //////////////////////////////////////////////////////////////////////////////
  823. void Prog_Done()
  824. {
  825.     //destroy minimap
  826.     DestroyMiniMap();
  827.  
  828.     //release frame buffer
  829.     LPDDS_Release(&lpddsFrame);
  830.  
  831.     //release main/back surfaces
  832.     LPDDS_Release(&lpddsMain);
  833.  
  834.     //release directdraw
  835.     LPDD_Release(&lpdd);
  836. }
  837.  
  838. //////////////////////////////////////////////////////////////////////////////
  839. //MAIN GAME LOOP
  840. //////////////////////////////////////////////////////////////////////////////
  841. void Prog_Loop()
  842. {
  843.     switch(iGameState)
  844.     {
  845.     case GS_STARTTURN://start the current team's turn
  846.         {
  847.             PUNITINFO pUnitInfo;//variable to check for the team's units
  848.             UNITLISTITER iter;//iterator for the main unit list
  849.             for(iter=MainUnitList.begin();iter!=MainUnitList.end();iter++)//iterate through the main unit list
  850.             {
  851.                 pUnitInfo=*iter;//grab the unit from the list
  852.                 if(pUnitInfo->iTeam==iCurrentTeam)//does this unit belong to the current team?
  853.                 {
  854.                     //give the unit a movement point
  855.                     pUnitInfo->iMovePoints=1+pUnitInfo->iType;
  856.                     //add this unit to the team list
  857.                     TeamUnitList.push_back(pUnitInfo);
  858.                 }
  859.             }
  860.             //set moved unit flag to false
  861.             bMovedUnit=false;
  862.             //set the next gamestate
  863.             iGameState=GS_NEXTUNIT;
  864.         }break;
  865.     case GS_NEXTUNIT://select the next unit as the current unit
  866.         {
  867.             //set current unit to NULL
  868.             pCurrentUnit=NULL;
  869.             if(TeamUnitList.empty())//if the team unit list is empty
  870.             {
  871.                 //if a unit has been moved, send to end of turn
  872.                 if(bMovedUnit)
  873.                 {
  874.                     iGameState=GS_ENDTURN;//end the turn
  875.                 }
  876.                 else
  877.                 {
  878.                     //send to GS_IDLE
  879.                     iGameState=GS_IDLE;//send to idle state
  880.                 }
  881.             }
  882.             else
  883.             {
  884.                 //turn is not over
  885.                 UNITLISTITER iter=TeamUnitList.begin();//get the first unit in the team list
  886.                 pCurrentUnit=*iter;//grab the unit from the list
  887.                 TeamUnitList.pop_front();//remove the unit from the list
  888.  
  889.                 //check to see if this unit is holding position
  890.                 if(pCurrentUnit->bHolding) return;//go to next unit
  891.  
  892.                 mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.remove(pCurrentUnit);//remove the unit from the map location
  893.                 mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.push_front(pCurrentUnit);//place unit at the top of the map locations unit list
  894.  
  895.                 POINT ptPlot=TilePlotter.PlotTile(pCurrentUnit->ptPosition);//plot the unit's location
  896.                 POINT ptScreen=Scroller.WorldToScreen(ptPlot);//translate into screen coordinates
  897.  
  898.                 if(!PtInRect(Scroller.GetScreenSpace(),ptScreen))//check to see if point is within screenspace
  899.                 {
  900.                     //not on screen
  901.                     ptPlot.x-=(Scroller.GetScreenSpaceWidth()/2);
  902.                     ptPlot.y-=(Scroller.GetScreenSpaceHeight()/2);
  903.                     //set the anchor
  904.                     Scroller.SetAnchor(&ptPlot);
  905.                     Renderer.AddRect(Scroller.GetScreenSpace());
  906.                 }
  907.                 iGameState=GS_IDLE;//set to idling gamestate
  908.             }
  909.         }break;
  910.     case GS_ENDTURN://end of a player's turn
  911.         {
  912.             //clear out team unit list(just to be sure)
  913.             TeamUnitList.clear();
  914.  
  915.             //change team
  916.             iCurrentTeam=1-iCurrentTeam;
  917.  
  918.             iGameState=GS_STARTTURN;//set gamestate to start next turn
  919.         }break;
  920.     case GS_NULLMOVE://do not move the current unit
  921.         {
  922.             //set the moved flag to true
  923.             bMovedUnit=true;
  924.             //set movement points to 0
  925.             pCurrentUnit->iMovePoints=0;
  926.             //don't really do anything, just go to the next unit
  927.             pCurrentUnit=NULL;
  928.             iGameState=GS_NEXTUNIT;
  929.         }break;
  930.     case GS_SKIPMOVE://skip this unit for now
  931.         {
  932.             //put unit at end of team unit list
  933.             TeamUnitList.push_back(pCurrentUnit);
  934.             pCurrentUnit=NULL;//set current unit to NULL
  935.             iGameState=GS_NEXTUNIT;//select the next unit
  936.         }break;
  937.     case GS_HOLDPOSITION://tell unit to hold position
  938.         {
  939.             //set holding flag
  940.             pCurrentUnit->bHolding=true;
  941.             //show the holding unit
  942.             Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y);
  943.             pCurrentUnit=NULL;
  944.             bFlash=true;
  945.             //set next gamestate
  946.             iGameState=GS_NEXTUNIT;
  947.             DWORD dwTimeStart=GetTickCount();//get the frame start time
  948.             //scroll the frame (0,0)
  949.             Renderer.ScrollFrame(0,0);
  950.             //update the frame
  951.             Renderer.UpdateFrame();
  952.             //flip to show the back buffer
  953.             lpddsMain->Flip(0,DDFLIP_WAIT);
  954.             //wait until 500 ms have passed
  955.             while(GetTickCount()-dwTimeStart<500);
  956.         }break;
  957.     case GS_STARTMOVE:
  958.         {
  959.             //set the moved flag to true
  960.             bMovedUnit=true;
  961.             //remove the unit from the map location
  962.             mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.remove(pCurrentUnit);
  963.             Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y);
  964.             //set next gamestate
  965.             iGameState=GS_DOMOVE;
  966.         }break;
  967.     case GS_DOMOVE:
  968.         {
  969.             //move the unit
  970.             pCurrentUnit->ptPosition=TileWalker.TileWalk(pCurrentUnit->ptPosition,idMoveUnit);
  971.             //set next gamestate
  972.             iGameState=GS_ENDMOVE;
  973.         }break;
  974.     case GS_ENDMOVE:
  975.         {
  976.             //place the unit on its new map location
  977.             mlMap[pCurrentUnit->ptPosition.x][pCurrentUnit->ptPosition.y].ulUnitList.push_front(pCurrentUnit);
  978.             Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y);
  979.             //subtract a movement point
  980.             pCurrentUnit->iMovePoints--;
  981.             //check to see if this unit is finished moving
  982.             if(pCurrentUnit->iMovePoints==0)
  983.             {
  984.                 //done moving
  985.                 pCurrentUnit=NULL;
  986.                 //set next gamestate
  987.                 iGameState=GS_NEXTUNIT;
  988.             }
  989.             else
  990.             {
  991.                 //not done moving
  992.                 iGameState=GS_IDLE;
  993.             }
  994.             bFlash=true;
  995.             DWORD dwTimeStart=GetTickCount();//get the frame start time
  996.             //scroll the frame (0,0)
  997.             Renderer.ScrollFrame(0,0);
  998.             //update the frame
  999.             Renderer.UpdateFrame();
  1000.             //flip to show the back buffer
  1001.             lpddsMain->Flip(0,DDFLIP_WAIT);
  1002.             //wait until 500 ms have passed
  1003.             while(GetTickCount()-dwTimeStart<500);
  1004.         }break;
  1005.     case GS_IDLE://the game is idling, update the frame, but thats about it.
  1006.         {
  1007.             DWORD dwTimeStart=GetTickCount();//get the frame start time
  1008.             //scroll the frame (0,0)
  1009.             Renderer.ScrollFrame(0,0);
  1010.  
  1011.             //toggle unit flash
  1012.             bFlash=!bFlash;
  1013.  
  1014.             //if the current unit is not null
  1015.             if(pCurrentUnit!=NULL)
  1016.             {
  1017.                 //add the tile in which the current unit lives
  1018.                 Renderer.AddTile(pCurrentUnit->ptPosition.x,pCurrentUnit->ptPosition.y);
  1019.             }
  1020.  
  1021.             //update the frame
  1022.             Renderer.UpdateFrame();
  1023.  
  1024.             //if the current unit IS null
  1025.             if(pCurrentUnit==NULL)
  1026.             {
  1027.                 //show the end of turn marker if bflash is true
  1028.                 if(bFlash) tsPressEnter.PutTile(lpddsBack,0,0,iCurrentTeam);
  1029.             }
  1030.  
  1031.             //redo the minimap
  1032.             UpdateMiniMap();
  1033.             RedrawMiniMap();
  1034.             ShowMiniMap();
  1035.  
  1036.             //flip to show the back buffer
  1037.             lpddsMain->Flip(0,DDFLIP_WAIT);
  1038.             //wait until 200 ms have passed
  1039.             while(GetTickCount()-dwTimeStart<200);
  1040.         }break;
  1041.     case GS_CLICKSELECT:
  1042.         {
  1043.             //check map location for emptiness
  1044.             if(mlMap[ptClick.x][ptClick.y].ulUnitList.empty())
  1045.             {
  1046.                 //map location is empty
  1047.                 iGameState=GS_CLICKCENTER;//we want to center on this map location
  1048.             }
  1049.             else
  1050.             {
  1051.                 //map location is not empty
  1052.                 //look at top of list
  1053.                 UNITLISTITER iter=mlMap[ptClick.x][ptClick.y].ulUnitList.begin();
  1054.                 PUNITINFO pUnitInfo=*iter;
  1055.                 //check that if this unit belongs to the current team
  1056.                 if(pUnitInfo->iTeam==iCurrentTeam)
  1057.                 {
  1058.                     //belongs to current team
  1059.                     //one unit?
  1060.                     if(mlMap[ptClick.x][ptClick.y].ulUnitList.size()==1)
  1061.                     {
  1062.                         //a single unit(already contained in pUnitInfo)
  1063.                         //is this the current unit?
  1064.                         if(pUnitInfo==pCurrentUnit)
  1065.                         {
  1066.                             //this is the current unit
  1067.                             iGameState=GS_IDLE;//return to the neutral gamestate
  1068.                         }
  1069.                         else
  1070.                         {
  1071.                             //this is not the current unit
  1072.                             //does this unit have any movement points left?
  1073.                             if(pUnitInfo->iMovePoints>0)
  1074.                             {
  1075.                                 //has movement points left
  1076.                                 //push the current unit to front of team list
  1077.                                 if(pCurrentUnit) TeamUnitList.push_front(pCurrentUnit);
  1078.                                 pCurrentUnit=NULL;
  1079.                                 //set holding to false for the new unit
  1080.                                 pUnitInfo->bHolding=false;
  1081.                                 //remove new unit from team list
  1082.                                 TeamUnitList.remove(pUnitInfo);
  1083.                                 //put new unit in front of team list
  1084.                                 TeamUnitList.push_front(pUnitInfo);
  1085.                                 //go to gs_nextunit
  1086.                                 iGameState=GS_NEXTUNIT;
  1087.                             }
  1088.                             else
  1089.                             {
  1090.                                 //does not have movement points left
  1091.                                 //go back to gs_idle
  1092.                                 iGameState=GS_IDLE;
  1093.                             }
  1094.                         }
  1095.                     }
  1096.                     else
  1097.                     {
  1098.                         //place the current unit at the front of the list
  1099.                         if(pCurrentUnit) TeamUnitList.push_front(pCurrentUnit);
  1100.                         //a stack of units
  1101.                         iGameState=GS_CLICKSTACK;
  1102.                     }
  1103.                 }
  1104.                 else
  1105.                 {
  1106.                     //does not belong to current team
  1107.                     iGameState=GS_CLICKCENTER;//we want to center on this map location
  1108.                 }
  1109.             }
  1110.         }break;
  1111.     case GS_CLICKCENTER:
  1112.         {
  1113.             //center on clicked tile
  1114.             POINT ptPlot=TilePlotter.PlotTile(ptClick);//plot tile
  1115.             ptPlot.x-=(Scroller.GetScreenSpaceWidth()/2);//adjust by half screenspace
  1116.             ptPlot.y-=(Scroller.GetScreenSpaceHeight()/2);
  1117.             Scroller.SetAnchor(&ptPlot);//set anchor
  1118.             Renderer.AddRect(Scroller.GetScreenSpace());
  1119.             //return to GS_IDLE
  1120.             iGameState=GS_IDLE;
  1121.         }break;
  1122.     case GS_CLICKSTACK:
  1123.         {
  1124.             //prepare the stack
  1125.             int count;
  1126.             for(count=0;count<20;count++)//clear out the list
  1127.                 SelectUnitList[count]=NULL;
  1128.             //reset count to 0
  1129.             count=0;
  1130.             //iterate through the list of units at the current map location
  1131.             UNITLISTITER iter;//iterator
  1132.             PUNITINFO pUnitInfo;//unit info
  1133.             for(iter=mlMap[ptClick.x][ptClick.y].ulUnitList.begin();count<20 && iter!=mlMap[ptClick.x][ptClick.y].ulUnitList.end();iter++)//iterate through list
  1134.             {
  1135.                 pUnitInfo=*iter;//grab the unit
  1136.                 //place unit in list
  1137.                 SelectUnitList[count]=pUnitInfo;
  1138.                 //add 1 to count
  1139.                 count++;
  1140.             }
  1141.             //send to next gamestate
  1142.             iGameState=GS_PICKUNIT;
  1143.         }break;
  1144.     case GS_PICKUNIT:
  1145.         {
  1146.             //no scrolling
  1147.             Renderer.ScrollFrame(0,0);
  1148.             //update frame
  1149.             Renderer.UpdateFrame();
  1150.             //place select window onto display
  1151.             DDBLTFX ddbltfx;
  1152.             DDBLTFX_Clear(&ddbltfx);
  1153.             ddbltfx.dwFillColor=dwSelectWindowColor;
  1154.             lpddsBack->Blt(&rcSelectWindow,NULL,NULL,DDBLT_WAIT | DDBLT_COLORFILL,&ddbltfx);
  1155.             //show the units
  1156.             int cellx,celly;//cell position
  1157.             int cellnum;//number of the cell
  1158.             int pixelx,pixely;//pixel position
  1159.             for(celly=0;celly<4;celly++)
  1160.             {
  1161.                 for(cellx=0;cellx<5;cellx++)
  1162.                 {
  1163.                     cellnum=cellx+celly*5;//calculate the cell number
  1164.                     //check that the unit exists
  1165.                     if(SelectUnitList[cellnum])
  1166.                     {
  1167.                         //calculate pixel position
  1168.                         pixelx=rcSelectWindow.left+ptCellSize.x*cellx;
  1169.                         pixely=rcSelectWindow.top+ptCellSize.y*celly;
  1170.                         //plot the units position
  1171.                         pixelx+=ptUnitOffset.x;
  1172.                         pixely+=ptUnitOffset.y;
  1173.                         //put the unit
  1174.                         tsUnit.PutTile(lpddsBack,pixelx,pixely,SelectUnitList[cellnum]->iType);
  1175.                         //move the pixel to place shielf
  1176.                         pixelx+=ptShieldOffset[SelectUnitList[cellnum]->iType].x;
  1177.                         pixely+=ptShieldOffset[SelectUnitList[cellnum]->iType].y;
  1178.                         //place appropriate shield
  1179.                         if(SelectUnitList[cellnum]->bHolding)
  1180.                         {
  1181.                             //unit is holding
  1182.                             tsShield.PutTile(lpddsBack,pixelx,pixely,iCurrentTeam*2+4);
  1183.                         }
  1184.                         else
  1185.                         {
  1186.                             //unit is not holding
  1187.                             if(SelectUnitList[cellnum]->iMovePoints)
  1188.                             {
  1189.                                 //unit has movement points left
  1190.                                 tsShield.PutTile(lpddsBack,pixelx,pixely,iCurrentTeam*2);
  1191.                             }
  1192.                             else
  1193.                             {
  1194.                                 //unit does not have movememtn points left
  1195.                                 tsShield.PutTile(lpddsBack,pixelx,pixely,iCurrentTeam*2+8);
  1196.                             }
  1197.                         }
  1198.                     }
  1199.                 }
  1200.             }
  1201.             //flip
  1202.             lpddsMain->Flip(0,DDFLIP_WAIT);
  1203.         }break;
  1204.     }
  1205. }
  1206.  
  1207. void RenderFunc(LPDIRECTDRAWSURFACE7 lpddsDst,RECT* rcClip,int xDst,int yDst,int xMap,int yMap)
  1208. {
  1209.     //put background tile
  1210.     tsBack.ClipTile(lpddsDst,rcClip,xDst,yDst,0);
  1211.     //check for an empty list
  1212.     if(!mlMap[xMap][yMap].ulUnitList.empty())
  1213.     {
  1214.         //list is not empty
  1215.         UNITLISTITER iter=mlMap[xMap][yMap].ulUnitList.begin();//get iterator to beginning of list
  1216.         PUNITINFO pUnitInfo=*iter;//grab the item
  1217.         //if this is the current unit
  1218.         if(pUnitInfo==pCurrentUnit)
  1219.         {
  1220.             //this is the current unit
  1221.             if(!bFlash) return;//if flash is "off" dont render
  1222.         }
  1223.         tsUnit.ClipTile(lpddsDst,rcClip,xDst,yDst,pUnitInfo->iType);//place the unit
  1224.         iter++;//move to the next item in the list
  1225.         if(iter==mlMap[xMap][yMap].ulUnitList.end())//if the end of the list, this is a single unit
  1226.         {
  1227.             if(pUnitInfo->bHolding)//if holding position
  1228.             {//holding
  1229.                 tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2+4);//place the shield
  1230.             }
  1231.             else
  1232.             {//not holding
  1233.                 tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2);//place the shield
  1234.             }
  1235.         }
  1236.         else//more than one unit... this is a stack
  1237.         {
  1238.             if(pUnitInfo->bHolding)//if holding position
  1239.             {//holding
  1240.                 tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2+5);//place the shield
  1241.             }
  1242.             else
  1243.             {//not holding
  1244.                 tsShield.ClipTile(lpddsDst,rcClip,xDst+ptShieldOffset[pUnitInfo->iType].x,yDst+ptShieldOffset[pUnitInfo->iType].y,pUnitInfo->iTeam*2+1);//place the shield
  1245.             }
  1246.         }
  1247.     }
  1248. }
  1249.  
  1250. void SetupMiniMap()//does initial setup of the minimap
  1251. {
  1252.     //create the surface
  1253.     lpddsMiniMap=LPDDS_CreateOffscreen(lpdd,160,80);
  1254.     //load in the tileset
  1255.     tsMiniMap.Load(lpdd,"minimapts.bmp");
  1256.     //initialize the minimap arrays
  1257.     MiniMap=new int[MAPWIDTH*MAPHEIGHT];
  1258.     for(int count=0;count<MAPWIDTH*MAPHEIGHT;count++)
  1259.     {
  1260.         MiniMap[count]=0;
  1261.     }
  1262.     //initialize iso components
  1263.     //tile plotter
  1264.     MiniTilePlotter.SetMapType(ISOMAP_DIAMOND);
  1265.     MiniTilePlotter.SetTileSize(4,2);
  1266.     //scroller
  1267.     //calc worldspace
  1268.     MiniScroller.CalcWorldSpace(&MiniTilePlotter,&tsMiniMap.GetTileList()[0].rcDstExt,MAPWIDTH,MAPHEIGHT);
  1269.     //set screen-space
  1270.     RECT rcMiniScreenSpace;
  1271.     SetRect(&rcMiniScreenSpace,0,0,160,80);
  1272.     MiniScroller.SetScreenSpace(&rcMiniScreenSpace);
  1273.     //set the anchor
  1274.     POINT ptAnchor;
  1275.     ptAnchor.x=MiniScroller.GetWorldSpace()->left;
  1276.     ptAnchor.y=MiniScroller.GetWorldSpace()->top;
  1277.     MiniScroller.SetAnchor(&ptAnchor);
  1278.  
  1279. }
  1280.  
  1281. void UpdateMiniMap()//updates the minimap array
  1282. {
  1283.     //upate the minimap array to reflect game status
  1284.     for(int y=0;y<MAPHEIGHT;y++)//loop through rows
  1285.     {
  1286.         for(int x=0;x<MAPWIDTH;x++)//loop through columns
  1287.         {
  1288.             MiniMap[y*MAPWIDTH+x]=0;
  1289.             //if empty, place a zero
  1290.             if(mlMap[x][y].ulUnitList.empty())
  1291.             {
  1292.                 MiniMap[y*MAPWIDTH+x]=0;
  1293.             }
  1294.             else
  1295.             {
  1296.                 //occupied by a player
  1297.                 UNITLISTITER iter=mlMap[x][y].ulUnitList.begin();
  1298.                 PUNITINFO pUnitInfo=*iter;
  1299.                 if(pUnitInfo!=pCurrentUnit || bFlash) MiniMap[y*MAPWIDTH+x]=1+pUnitInfo->iTeam;//put team info into minimap array
  1300.             }
  1301.         }
  1302.     }
  1303. }
  1304.  
  1305. void RedrawMiniMap()//redraws the minimap to reflect current gamestate
  1306. {
  1307.     //redraw the entire minimap
  1308.     //in real code, you can keep two arrays
  1309.     //one valid this frame and one valid last frame
  1310.     //and then only blit the changes from last frame to this one
  1311.     //thus reducing the number of blits
  1312.     //clear out the minimap surface
  1313.     DDBLTFX ddbltfx;
  1314.     DDBLTFX_ColorFill(&ddbltfx,0);
  1315.     lpddsMiniMap->Blt(NULL,NULL,NULL,DDBLT_WAIT | DDBLT_COLORFILL,&ddbltfx);
  1316.     POINT ptPlot;//plotting point
  1317.     for(int y=0;y<MAPHEIGHT;y++)//loop through rows
  1318.     {
  1319.         for(int x=0;x<MAPWIDTH;x++)//loop through columns
  1320.         {
  1321.             //put the mini-tile
  1322.             ptPlot.x=x;
  1323.             ptPlot.y=y;
  1324.             //plot map position
  1325.             ptPlot=MiniTilePlotter.PlotTile(ptPlot);
  1326.             //convert to screen coords
  1327.             ptPlot=MiniScroller.WorldToScreen(ptPlot);
  1328.             tsMiniMap.PutTile(lpddsMiniMap,ptPlot.x,ptPlot.y,MiniMap[y*MAPWIDTH+x]);
  1329.         }
  1330.     }
  1331.     //show a rectangle around viewable area on minimap
  1332.     HDC hdc;
  1333.     lpddsMiniMap->GetDC(&hdc);//borrow the dc from the minimap surface
  1334.  
  1335.     //get the anchor point from the main scroller
  1336.     POINT ptAnchor;
  1337.     ptAnchor.x=Scroller.GetAnchor()->x;
  1338.     ptAnchor.y=Scroller.GetAnchor()->y;
  1339.  
  1340.     //scale down by a factor of 16(to go from a 64x32 tile to 4x2)
  1341.     ptAnchor.x/=16;
  1342.     ptAnchor.y/=16;
  1343.  
  1344.     //subtract the mini-scroller's upper left
  1345.     ptAnchor.x-=MiniScroller.GetWorldSpace()->left;
  1346.     ptAnchor.y-=MiniScroller.GetWorldSpace()->top;
  1347.  
  1348.     //move to position in minimap
  1349.     MoveToEx(hdc,ptAnchor.x,ptAnchor.y,NULL);
  1350.     //draw box, one line at a time
  1351.     LineTo(hdc,ptAnchor.x+30,ptAnchor.y);//30 is 480/16
  1352.     LineTo(hdc,ptAnchor.x+30,ptAnchor.y+30);
  1353.     LineTo(hdc,ptAnchor.x,ptAnchor.y+30);
  1354.     LineTo(hdc,ptAnchor.x,ptAnchor.y);
  1355.  
  1356.     lpddsMiniMap->ReleaseDC(hdc);//restore the dc to the minimap
  1357. }
  1358.  
  1359. void ShowMiniMap()//shows the minimap on-screen
  1360. {
  1361.     RECT rcSrc;//src blitting rect
  1362.     RECT rcDst;//destination blitting rect
  1363.     //set src rect
  1364.     SetRect(&rcSrc,0,0,160,80);
  1365.     //set dest rect
  1366.     CopyRect(&rcDst,&rcSrc);
  1367.     OffsetRect(&rcDst,480,0);
  1368.     //do the blit
  1369.     lpddsBack->Blt(&rcDst,lpddsMiniMap,&rcSrc,DDBLT_WAIT,NULL);
  1370. }
  1371.  
  1372. void DestroyMiniMap()//cleans up the minimap
  1373. {
  1374.     //delete minimap array
  1375.     delete[] MiniMap;
  1376.     //destroy minimap surface
  1377.     if(lpddsMiniMap)
  1378.     {
  1379.         lpddsMiniMap->Release();
  1380.         lpddsMiniMap=NULL;
  1381.     }
  1382.     //destroy minimap tileset
  1383.     tsMiniMap.Unload();
  1384. }
  1385.  
  1386.